/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2005
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
* 
* Author's email :    insane@dees.ufmg.br
* Author's website :  http://www.dees.ufmg.br/insane
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package br.ufmg.dees.insane.model.disc.analysismodel;

import java.text.DecimalFormat;
import java.util.ListIterator;

import br.ufmg.dees.insane.crossSection.CrossSection;
import br.ufmg.dees.insane.materialMedia.material.LinearElasticIsotropic;
import br.ufmg.dees.insane.model.disc.element.FrameElement;
import br.ufmg.dees.insane.model.disc.element.force.ElementForce;
import br.ufmg.dees.insane.model.disc.fem.FemModel;
import br.ufmg.dees.insane.model.disc.node.Node;
import br.ufmg.dees.insane.model.disc.pointforce.PointForce;
import br.ufmg.dees.insane.model.disc.shape.Shape1DCart;
import br.ufmg.dees.insane.util.IMatrix;
import br.ufmg.dees.insane.util.IVector;

/**
*A class representing the analysis model of a space frame element.<p>
*
*The local Y axis of a space frame element is determined as the cross product of global Z axis and local X axis.<br>
*The local Z axis of a space frame element is perpedicular to the local X and local Y axis.<br>
*If the local X axis coincides with the global Z axis, the local Y axis is selected as the global Y axis.
*
*@author Fonseca, Flavio & Pitangueira, Roque
*@version 1.0
*@since September 2003
*@see model.discrete.element.FrameElement
*/


public class SpaceFrame extends FrameElmAnalysis {
	
	private static final long serialVersionUID = 1L;

	/** Constructs a SpaceFrame analysis model, sets its ndf to 6 and its valid equations to true in all positions.*/
	public SpaceFrame() {
		type = "SpaceFrame";
		ndf = 6;
		validEquations[0] = true;
		validEquations[1] = true;
		validEquations[2] = true;
		validEquations[3] = true;
		validEquations[4] = true;
		validEquations[5] = true;
		dominion = 1;
	};
	
//********************************************************************************
	
	/** Returns the type of the Shape adequate to this type of AnalysisModel.
	*@return The type of the Shape adequate to this type of AnalysisModel.
	*/
	public String getDefaultShapeType() {
		return ("LinearCubic1DCart");
	}
	
//********************************************************************************
	
	/** Returns the nodal rotation matrix of the specified FrameElement, accordingly to this AnalysisModel type.
	*@param elm The FrameElement whose nodal rotation matrix is desired.
	*@return The nodal rotation matrix of the specified FrameElement, accordingly to this AnalysisModel type.
	*/
	public IMatrix nodalRotationMatrix(FrameElement elm) {
		//A SpaceFrame element do not accept inclined supports (in this program)
		IMatrix mat = new IMatrix(12, 12);
		return (mat);
	};
	
//********************************************************************************
	
	/** Returns the local stiffness matrix of the specified FrameElement, accordingly to this AnalysisModel type.<br>
	*This matrix does not consider the possible liberations at the extremities of the FrameElement.
	*@param elm The FrameElement whose local stiffness matrix is desired.
	*@return The local stiffness matrix of the specified FrameElement, accordingly to this AnalysisModel type.
	*/
	public IMatrix localStiffnessMatrix(FrameElement elm){
		IMatrix mat = new IMatrix(12,12);
		mat.setZero();
		LinearElasticIsotropic elmMaterial = (LinearElasticIsotropic) elm.getMaterial();
		CrossSection elmCrossSection = elm.getCrossSection();
		
		//Setting the value of the variable e, a, l, ix, iy and iz
		double e = elmMaterial.getMaterialValues(elmMaterial.ELASTICITY).doubleValue();
		double g = elmMaterial.getMaterialValues(elmMaterial.SHEAR_MODULUS).doubleValue();
		double a = elmCrossSection.getArea();
		double l = elm.getLength();
		double ix = elmCrossSection.getIx();
		double iy = elmCrossSection.getIy();
		double iz = elmCrossSection.getIz();
		
		//Setting the value of the upper triangular part of the matrix
		mat.setElement(0, 0, a * e / l);
		mat.setElement(1, 1, 12 * e * iz / (l*l*l));
		mat.setElement(2, 2, 12 * e *iy /(l*l*l));
		mat.setElement(3, 3, g * ix / l);
		mat.setElement(2, 4, -6 * e * iy / (l*l));
		mat.setElement(4, 4, 4 * e * iy / l);
		mat.setElement(1, 5, 6 * e * iz / (l*l));
		mat.setElement(5, 5, 4 * e * iz / l);
		mat.setElement(0, 6, -a * e / l);
		mat.setElement(6, 6, a * e / l);
		mat.setElement(1, 7, -12 * e * iz / (l*l*l));
		mat.setElement(5, 7, -6 * e * iz / (l*l));
		mat.setElement(7, 7, 12 * e * iz / (l*l*l));
		mat.setElement(2, 8, -12 * e * iy /(l*l*l));
		mat.setElement(4, 8, 6 * e * iy / (l*l));
		mat.setElement(8, 8, 12 * e * iy / (l*l*l));
		mat.setElement(3, 9, -g * ix / l);
		mat.setElement(9, 9, g * ix / l);
		mat.setElement(2, 10, -6 * e * iy / (l*l));
		mat.setElement(4, 10, 2 * e * iy /l);
		mat.setElement(8, 10, 6 * e * iy / (l*l));
		mat.setElement(10, 10, 4 * e * iy /l);
		mat.setElement(1, 11, 6 * e * iz / (l*l));
		mat.setElement(5, 11, 2 * e * iz / l);
		mat.setElement(7, 11, -6 * e * iz / (l*l));
		mat.setElement(11, 11, 4 * e * iz / l);
		
		//Setting the value of the lower triangular part od the matrix (symmetry)
		for (int i=0; i<12; i++)
			for (int j=0; j<i; j++)
				mat.setElement(i, j, mat.getElement(j, i));
		return (mat);
		
	};
	
//********************************************************************************
	
	/** Returns the corrected local stiffness matrix of the specified FrameElement, accordingly to this AnalysisModel type.
	*This matrix is corrected to consider the liberations at the extremities of the FrameElement.
	*@param elm The FrameElement whose corrected local stiffness matrix is desired.
	*@return The corrected local stiffness matrix of the specified FrameElement, accordingly to this AnalysisModel type.
	*/
	public IMatrix localCorrectedStiffnessMatrix(FrameElement elm) {
		
		IMatrix mat = new IMatrix(12, 12);
		IMatrix mat2 = new IMatrix(12, 12);
		mat2.setZero();
		
		mat = localStiffnessMatrix(elm);
		
		//Changing stiffness matrix because of liberations at extremity
		int l=-1;
		double kmj, klj, kll, kml;
		for (int i=0; i<12; i++) {
			if (i<6 && getValidEquation(i))
				l++;
			if (i>5 && getValidEquation(i-6))
				l++;
			if (elm.getLiberation(i)) {
				kll = mat.getElement(l, l);
				for (int j=0; j<12; j++) {
					for (int m=0; m<12; m++) {
						kmj = mat.getElement(m, j);
						klj = mat.getElement(l ,j);
						kml = mat.getElement(m, l);
						mat2.setElement(m, j, kmj - (klj / kll) * kml);
					};
				};
			mat.set(mat2);
			};
		};
		
		return (mat);
	};

//********************************************************************************
	
	/** Returns the transformation matrix of the specified FrameElement, accordingly to this AnalysisModel type.
	*@param elm The FrameElement whose transformation matrix is desired.
	*@return The transformation matrix of the specified FrameElement, accordingly to this AnalysisModel type.
	*/
	public IMatrix transformationMatrix(FrameElement elm){
		IMatrix mat = new IMatrix(12,12);
		mat.setZero();
		IMatrix aux = new IMatrix(3,3);
		aux.setZero();
		Node elmInitialNode = elm.getInitialNode();
		Node elmFinalNode = elm.getFinalNode();
		
		//Setting the value of the variable l, m, n and d
		double l = (elmFinalNode.getCoord().x - elmInitialNode.getCoord().x) / elm.getLength();
		double m = (elmFinalNode.getCoord().y - elmInitialNode.getCoord().y) / elm.getLength();
		double n = (elmFinalNode.getCoord().z - elmInitialNode.getCoord().z) / elm.getLength();
		double d = Math.sqrt(Math.pow(l, 2) + Math.pow(m, 2));
		
		//Setting the value of the matrix for an element whose local x axis is not parallel to the global z axis
		if (n!=1 && n!=-1) {
			//Setting the value of the aux matrix
			aux.setElement(0, 0, l);
			aux.setElement(0, 1, m);
			aux.setElement(0, 2, n);
			aux.setElement(1, 0, -m/d);
			aux.setElement(1, 1, l/d);
			aux.setElement(2, 0, -l*n/d);
			aux.setElement(2, 1, -m*n/d);
			aux.setElement(2, 2, d);
			
			//Inserting the aux matrix at the postions (0,0), (3,3), (6,6) and (9,9) of the matrix
			aux.copySubMatrix(0, 0, 3, 3, 0, 0, mat);
			aux.copySubMatrix(0, 0, 3, 3, 3, 3, mat);
			aux.copySubMatrix(0, 0, 3, 3, 6, 6, mat);
			aux.copySubMatrix(0, 0, 3, 3, 9, 9, mat);
		}
		
		//Setting the value of the matrix for an element whose positive local x axis is parallel to the global positive z axis
		else if(n==1) {
			//Setting the value of the aux matrix
			aux.setElement(0, 2, 1);
			aux.setElement(1, 1, 1);
			aux.setElement(2, 0, -1);
			
			//Inserting the aux matrix at the postions (0,0), (3,3), (6,6) and (9,9) of the matrix
			aux.copySubMatrix(0, 0, 3, 3, 0, 0, mat);
			aux.copySubMatrix(0, 0, 3, 3, 3, 3, mat);
			aux.copySubMatrix(0, 0, 3, 3, 6, 6, mat);
			aux.copySubMatrix(0, 0, 3, 3, 9, 9, mat);
		}
		
		//Setting the value of the matrix for an element whose positive local x axis opposites the global positive z axis
		else {
			//Setting the value of the aux matrix
			aux.setElement(0, 0, -1);
			aux.setElement(1, 1, 0);
			aux.setElement(2, 0, 1);
			
			//Inserting the aux matrix at the postions (0,0), (3,3), (6,6) and (9,9) of the matrix
			aux.copySubMatrix(0, 0, 3, 3, 0, 0, mat);
			aux.copySubMatrix(0, 0, 3, 3, 3, 3, mat);
			aux.copySubMatrix(0, 0, 3, 3, 6, 6, mat);
			aux.copySubMatrix(0, 0, 3, 3, 9, 9, mat);
		};
		
		return (mat);
	};
	
//********************************************************************************
	
	/** Mounts the shape function matrix accordingly to this AnalysisModel type with the values of two IVectors.<br>
	*The first IVector must contain the shape functions values which will be put in the upper part of the matrix.<br>
	*The second IVector must contain the shape functions values which will be put in the lower part of the matrix (related to the derivated shape functions).
	*@param n1 A IVector containing the shape functions values which will be put in the upper part of the matrix.
	*@param n2 A IVector containing the shape functions values which will be put in the upper part of the matrix.
	*@return The shape function matrix accordingly to this AnalysisModel type.
	*/
	public IMatrix mountMatrixN(IVector n1, IVector n2) {
		IMatrix n = new IMatrix(6, 12);
		n.setZero();
		n.setElement(0, 0, n1.getElement(0));
		n.setElement(1, 1, n1.getElement(2));
		n.setElement(2, 2, n1.getElement(2));
		n.setElement(1, 5, n1.getElement(3));
		n.setElement(2, 4, -n1.getElement(3));
		n.setElement(3, 3, n1.getElement(0));
		n.setElement(4, 2, n2.getElement(2));
		n.setElement(5, 1, n2.getElement(2));
		n.setElement(4, 4, -n2.getElement(3));
		n.setElement(5, 5, n2.getElement(3));
		n.setElement(0, 6, n1.getElement(1));
		n.setElement(1, 7, n1.getElement(4));
		n.setElement(2, 8, n1.getElement(4));
		n.setElement(2, 10, -n1.getElement(5));
		n.setElement(1, 11, n1.getElement(5));
		n.setElement(3, 9, n1.getElement(1));
		n.setElement(4, 8, n2.getElement(4));
		n.setElement(5, 7, n2.getElement(4));
		n.setElement(4, 10, -n2.getElement(5));
		n.setElement(5, 11, n2.getElement(5));
		return (n);
	};
	
//********************************************************************************
	
	/** Mounts the shape function matrix accordingly to this AnalysisModel type with the values of one IVector.<br>
	*The IVector must contain the shape functions values which will be put in the upper part of the matrix.<br>
	*The value zero will be put in the lower part of the matrix (related to the derivated shape functions).
	*@param n1 A IVector containing the shape functions values which will be put in the upper part of the matrix.
	*@return The shape function matrix accordingly to this AnalysisModel type.
	*/
	public IMatrix mountMatrixN(IVector n1) {
		IMatrix n = new IMatrix(6, 12);
		n.setZero();
		n.setElement(0, 0, n1.getElement(0));
		n.setElement(1, 1, n1.getElement(2));
		n.setElement(2, 2, n1.getElement(2));
		n.setElement(1, 5, n1.getElement(3));
		n.setElement(2, 4, -n1.getElement(3));
		n.setElement(3, 3, n1.getElement(0));
		n.setElement(0, 6, n1.getElement(1));
		n.setElement(1, 7, n1.getElement(4));
		n.setElement(2, 8, n1.getElement(4));
		n.setElement(2, 10, -n1.getElement(5));
		n.setElement(1, 11, n1.getElement(5));
		n.setElement(3, 9, n1.getElement(1));
		return (n);
	};
	
//********************************************************************************
	
	/** Returns the local equivalent nodal force vector of the specified FrameElement, accordingly to this AnalysisModel type.
	*This vector does not consider the possible liberations at the extremities of the FrameElement.
	*@param elm The FrameElement whose local equivalent nodal force vector is desired.
	*@return The local equivalent nodal force vector of the specified FrameElement, accordingly to this AnalysisModel type.
	*/
	public IVector localEquivalentForceVector(FrameElement elm) {
		IVector lEqForce = new IVector(12);
		IVector a = new IVector(6);
		IVector b = new IVector(6);
		IVector v1;
		IVector v2;
		IVector f1 = new IVector(12);
		IVector f2 = new IVector(12);
		IMatrix n;
		IMatrix n1;
		IMatrix n2;
		Shape1DCart shp = (Shape1DCart) elm.getShape();
		
		//Calculating equivalent forces for elmPointForces
		PointForce elmPF;
		ListIterator elmPFs = elm.getElmPointForcesList().listIterator();
		while (elmPFs.hasNext()) {
			elmPF = (PointForce) elmPFs.next();
			
			//Setting the values for a vector (fe = N.transpose() * a)
			a.setElement(0, elmPF.getForce(0));
			a.setElement(1, elmPF.getForce(1));
			a.setElement(2, elmPF.getForce(2));
			a.setElement(3, elmPF.getForce(3));
			a.setElement(4, elmPF.getForce(4));
			a.setElement(5, elmPF.getForce(5));
			
			v1 = shp.getShapeFunction(elm, elmPF.getCoord().x);
			v2 = shp.getDerivedShapeFunction(elm, elmPF.getCoord().x);
			n = mountMatrixN(v1, v2);
			n.transpose();
			f1.mul(n, a);
			lEqForce.add(f1);
		};
		
		//Calculating equivalent forces for elementForces
		a.zero();
		ElementForce bf;
		PointForce pf1;
		PointForce pf2;
		ListIterator bfs = elm.getElmLineForcesList().listIterator();
		while (bfs.hasNext()) {
			bf = (ElementForce) bfs.next();
			pf1 = (PointForce) bf.getPointForcesList().get(0);
			pf2 = (PointForce) bf.getPointForcesList().get(1);
			
			//Setting the values of the vectors a and b (lEFV = IntegratedShapeFunctionMultX.transpose() * a + IntegratedShapeFunction.transpose() * b)
			a.setElement(0, (pf2.getForce(0) - pf1.getForce(0)) / (pf2.getCoord().x - pf1.getCoord().x));
			a.setElement(1, (pf2.getForce(1) - pf1.getForce(1)) / (pf2.getCoord().x - pf1.getCoord().x));
			a.setElement(2, (pf2.getForce(2) - pf1.getForce(2)) / (pf2.getCoord().x - pf1.getCoord().x));
			a.setElement(3, (pf2.getForce(3) - pf1.getForce(3)) / (pf2.getCoord().x - pf1.getCoord().x));
			a.setElement(4, 0);
			a.setElement(5, 0);
			
			b.setElement(0, -(a.getElement(0)) * pf2.getCoord().x + pf2.getForce(0));
			b.setElement(1, -(a.getElement(1)) * pf2.getCoord().x + pf2.getForce(1));
			b.setElement(2, -(a.getElement(2)) * pf2.getCoord().x + pf2.getForce(2));
			b.setElement(3, -(a.getElement(3)) * pf2.getCoord().x + pf2.getForce(3));
			b.setElement(2, 0);
			b.setElement(2, 0);
			
			v1 = shp.getIntegratedShapeFunctionMultX(elm, pf1.getCoord().x, pf2.getCoord().x);
			v2 = shp.getIntegratedShapeFunction(elm, pf1.getCoord().x, pf2.getCoord().x);
			n1 = mountMatrixN(v1);
			n2 = mountMatrixN(v2);
			n1.transpose();
			n2.transpose();
			f1.mul(n1, a);
			f2.mul(n2, b);
			f1.add(f2);
			lEqForce.add(f1);
		};
		
		//Calculating equivalent forces for temperature changes
		LinearElasticIsotropic mat = (LinearElasticIsotropic) elm.getMaterial();
		CrossSection cs = elm.getCrossSection();
		f1.zero();
		if (cs.getHeight()!=0) {
			double factor1 = mat.getMaterialValues(mat.ELASTICITY).doubleValue() * mat.getMaterialValues(mat.THERMAL_COEFF).doubleValue() * cs.getArea() * elm.getDeltaTemp(1);
			double factor2 = mat.getMaterialValues(mat.ELASTICITY).doubleValue() * mat.getMaterialValues(mat.THERMAL_COEFF).doubleValue() * cs.getIz() / cs.getHeight() * (elm.getDeltaTemp(2) - elm.getDeltaTemp(0));
			f1.setElement(0, -factor1);
			f1.setElement(5, -factor2);
			f1.setElement(6, factor1);
			f1.setElement(11, factor2);
			lEqForce.add(f1);
		}
		
		//Calculating equivalent forces for prescribed displacements on elements
		f1.zero();
		n1 = localCorrectedStiffnessMatrix(elm);
		v1 = elm.getElmPreDisplacement();
		f2.zero();
		int j = 0;
		for (int i=0; i<6; i++) {
			if (validEquations[i]) {
				f2.setElement(j, v1.getElement(i));
				f2.setElement(j+6, v1.getElement(i+6));
				j++;
			};
		};
		f1.mul(n1, f2);
		lEqForce.add(f1);
		
		//Adding initial nodal force
		f1.setElement(0, elm.getInitialNodalForce(0));
		f1.setElement(1, elm.getInitialNodalForce(1));
		f1.setElement(2, elm.getInitialNodalForce(2));
		f1.setElement(3, elm.getInitialNodalForce(3));
		f1.setElement(4, elm.getInitialNodalForce(4));
		f1.setElement(5, elm.getInitialNodalForce(5));
		f1.setElement(6, elm.getInitialNodalForce(6));
		f1.setElement(7, elm.getInitialNodalForce(7));
		f1.setElement(8, elm.getInitialNodalForce(8));
		f1.setElement(9, elm.getInitialNodalForce(9));
		f1.setElement(10, elm.getInitialNodalForce(10));
		f1.setElement(11, elm.getInitialNodalForce(11));
		lEqForce.add(f1);
		
		return (lEqForce);
	};
	
//********************************************************************************
	
	/** Returns the corrected local equivalent nodal force vector of the specified FrameElement, accordingly to this AnalysisModel type.
	*This matrix is corrected to consider the liberations at the extremities of the FrameElement.
	*@param elm The FrameElement whose corrected local equivalent nodal force vector is desired.
	*@return The corrected local equivalent nodal force vector of the specified FrameElement, accordingly to this AnalysisModel type.
	*/
	public IVector localCorrectedEquivalentForceVector(FrameElement elm) {
		
		IVector vec = new IVector(12);
		vec = localEquivalentForceVector(elm);
		
		IVector vec2 = new IVector(12);
		
		IMatrix mat = new IMatrix (12, 12);
		mat = localStiffnessMatrix(elm);
		
		IMatrix mat2 = new IMatrix(12, 12);
		mat2.setZero();
		
		int l=-1;
		double kmj, klj, kll, kml, fl, fm;;
		for (int i=0; i<12; i++) {
			if (i<6 && getValidEquation(i))
				l++;
			if (i>5 && getValidEquation(i-6))
				l++;
			if (elm.getLiberation(i)) {
				kll = mat.getElement(l, l);
				fl = vec.getElement(l);
				
				for (int j=0; j<12; j++) {
					for (int m=0; m<12; m++) {
						kmj = mat.getElement(m, j);
						klj = mat.getElement(l ,j);
						kml = mat.getElement(m, l);
						fm = vec.getElement(m);
						vec2.setElement(m, kml / kll * fl - fm);
						mat2.setElement(m, j, kmj - (klj / kll) * kml);
					};
				};
				vec2.negate();
				vec.set(vec2);
				mat.set(mat2);
			};
		};
		
		return (vec);
		
	};
	
//********************************************************************************
	
	/** Prints in the screen a table with the valid nodal displacements for this AnalysisModel type.
	*@param mod The FemModel which contains the data to be printed.
	*/
	public void printDisplacements(FemModel mod) {
		DecimalFormat fmt = new DecimalFormat();
		fmt.applyPattern("0.000E00");
		Node node;
		ListIterator nodes = mod.getNodesList().listIterator();
		System.out.println("NODE DISPLACEMENTS:");
		System.out.println("Node" +"\t"+ "Dx" +"\t\t"+ "Dy" +"\t\t"+ "Dz" +"\t\t"+ "Rx" +"\t\t"+ "Ry" +"\t\t"+ "Rz");
		while (nodes.hasNext()) {
			node = (Node) nodes.next();
			System.out.println("Node " + node.getLabel() +"\t"+ fmt.format(node.getDisplacement(0)) +"\t\t"+ fmt.format(node.getDisplacement(1)) +"\t\t"+ fmt.format(node.getDisplacement(2)) +"\t\t"+ fmt.format(node.getDisplacement(3)) +"\t\t"+ fmt.format(node.getDisplacement(4)) +"\t\t"+ fmt.format(node.getDisplacement(5)));
		};
		System.out.println("\t");
	};
	
//********************************************************************************
	
	/** Prints in the screen a table with the valid actions at elements' extremities for this AnalysisModel type.
	*@param mod The FemModel which contains the data to be printed.
	*/
	public void printActionsAtExtremities(FemModel mod) {
		DecimalFormat fmt = new DecimalFormat();
		fmt.applyPattern("####0.000");
		System.out.println("ACTIONS AT ELEMENTS' EXTREMITIES:");
		FrameElement elm;
		ListIterator elms = mod.getElementsList().listIterator();
		System.out.println("\t|----------------------------------------------- INITIAL NODE -----------------------------------------------|");
		System.out.println("Elm" +"\t"+ "Fx" +"\t\t"+ "Fy" +"\t\t"+ "Fz" +"\t\t"+ "Mx" +"\t\t"+ "My" +"\t\t"+ "Mz");
		while (elms.hasNext()) {
			elm = (FrameElement) elms.next();
			System.out.println("Elm " + elm.getLabel() +"\t"+ fmt.format(elm.getActionAtExtremity(0)) +"\t\t"+ fmt.format(elm.getActionAtExtremity(1)) +"\t\t"+ fmt.format(elm.getActionAtExtremity(2)) +"\t\t"+ fmt.format(elm.getActionAtExtremity(3)) +"\t\t"+ fmt.format(elm.getActionAtExtremity(4)) +"\t\t"+ fmt.format(elm.getActionAtExtremity(5)));
		};
		elms = mod.getElementsList().listIterator();
		System.out.println("\t|------------------------------------------------ FINAL NODE ------------------------------------------------|");
		System.out.println("Elm" +"\t"+ "Fx" +"\t\t"+ "Fy" +"\t\t"+ "Fz" +"\t\t"+ "Mx" +"\t\t"+ "My" +"\t\t"+ "Mz");
		while (elms.hasNext()) {
			elm = (FrameElement) elms.next();
		System.out.println("Elm " + elm.getLabel() +"\t"+ fmt.format(elm.getActionAtExtremity(6)) +"\t\t"+ fmt.format(elm.getActionAtExtremity(7)) +"\t\t"+ fmt.format(elm.getActionAtExtremity(8)) +"\t\t"+ fmt.format(elm.getActionAtExtremity(9)) +"\t\t"+ fmt.format(elm.getActionAtExtremity(10)) +"\t\t"+ fmt.format(elm.getActionAtExtremity(11)));
		};
		System.out.println("\t");
	};
	
//********************************************************************************
	
	/** Prints in the screen a table with the valid support reactions for this AnalysisModel type.
	*@param mod The FemModel which contains the data to be printed.
	*/
	public  void printReactions(FemModel mod) {
		DecimalFormat fmt = new DecimalFormat();
		fmt.applyPattern("####0.000");
		Node node;
		
		//Forces on inelastic supports
		ListIterator nodes = mod.getNodesList().listIterator();
		System.out.println("REACTIONS ON INELASTIC SUPPORTS:");
		System.out.println("Node" +"\t"+ "Fx" +"\t\t"+ "Fy" +"\t\t"+ "Fz" +"\t\t"+ "Mx" +"\t\t"+ "My" +"\t\t"+ "Mz");
		boolean anyRestraint = false;
		while (nodes.hasNext()) {
			node = (Node) nodes.next();
			boolean hasRestraint = false;
			for (int i=0; i<6; i++)
				if (node.getRestraint(i)) {
					hasRestraint = true;
					anyRestraint = true;
				};
			if (hasRestraint)
				System.out.println("Node " + node.getLabel() +"\t"+ fmt.format(node.getReaction(0)) +"\t\t"+ fmt.format(node.getReaction(1)) +"\t\t"+ fmt.format(node.getReaction(2)) +"\t\t"+ fmt.format(node.getReaction(3)) +"\t\t"+ fmt.format(node.getReaction(4)) +"\t\t"+ fmt.format(node.getReaction(5)));
		};
		if (!anyRestraint)
			System.out.println("THERE IS NO INELASTIC SUPPORT.");
		System.out.println("\t");
		
		//Forces on elastic supports
		nodes = mod.getNodesList().listIterator();
		System.out.println("REACTIONS ON ELASTIC SUPPORTS:");
		System.out.println("Node" +"\t"+ "Fx" +"\t\t"+ "Fy" +"\t\t"+ "Fz" +"\t\t"+ "Mx" +"\t\t"+ "My" +"\t\t"+ "Mz");
		boolean anySpring = false;
		while (nodes.hasNext()) {
			node = (Node) nodes.next();
			boolean hasSpring = false;
			double[] eReac = new double[6];
			for (int i=0; i<6; i++)
			if (node.getSpring(i)!=0) {
				hasSpring = true;
				anySpring = true;
				eReac[i] = - node.getDisplacement(i) * node.getSpring(i);
			};
			if (hasSpring)
				System.out.println("Node " + node.getLabel() +"\t"+ fmt.format(eReac[0]) +"\t\t"+ fmt.format(eReac[1]) +"\t\t"+ fmt.format(eReac[2]) +"\t\t"+ fmt.format(eReac[3]) +"\t\t"+ fmt.format(eReac[4]) +"\t\t"+ fmt.format(eReac[5]));
		};
		if (!anySpring)
			System.out.println("THERE IS NO ELASTIC SUPPORT.");
		System.out.println("\t");
	};
	
//********************************************************************************

}
